/*
 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include <aos/aos.h>
#include <aos/yloop.h>
#include "netmgr.h"
#include "iot_export.h"
#include "iot_import.h"
#include "app_entry.h"
#include "aos/kv.h"
#include "vendor.h"
#include "device_state_manger.h"
#include "smart_outlet.h"
#include "msg_process_center.h"
#include "property_report.h"

#include "lj.h"
#include "lj_proto_usart.h"

#if defined(AOS_TIMER_SERVICE)||defined(AIOT_DEVICE_TIMER_ENABLE)
#include "iot_export_timer.h"
#endif
#ifdef CSP_LINUXHOST
#include <signal.h>
#endif

#include <k_api.h>

#if defined(OTA_ENABLED) && defined(BUILD_AOS)
#include "ota_service.h"
#endif

#ifdef EN_COMBO_NET
#include "breeze_export.h"
#include "combo_net.h"
#endif

static aos_task_t task_key_detect;
static aos_task_t task_msg_process;
static aos_task_t task_property_report;
static aos_task_t task_linkkit_reset;
static aos_task_t task_reboot_device;

static char linkkit_started = 0;
static char awss_dev_ap_started = 0;

extern int init_awss_flag(void);

void do_awss_active(void);

#ifdef CONFIG_PRINT_HEAP
void print_heap()
{
    extern k_mm_head *g_kmm_head;
    int               free = g_kmm_head->free_size;
    app_info("============free heap size =%d==========", free);
}
#endif

static void wifi_service_event(input_event_t *event, void *priv_data)
{
    if (event->type != EV_WIFI) {
        return;
    }

#ifdef zzc
   if (event->code == CODE_WIFI_ON_GOT_IP) {
       all_wifi=1;
	    lj_usend_wifi_net_status(all_wifi,all_net);
       LOG("lj_rece CODE_WIFI_ON_CONNECTED..................");
   }
   else if(event->code == CODE_WIFI_ON_DISCONNECT)
   {
       all_wifi=0;
       all_net=0;
	    lj_usend_wifi_net_status(all_wifi,all_net);
       LOG("lj_rece CODE_WIFI_ON_DISCONNECT..................");
   }
#endif

    app_info("wifi_service_event(), event->code=%d", event->code);
    if (event->code == CODE_WIFI_ON_CONNECTED) {
        app_info("CODE_WIFI_ON_CONNECTED");
#ifdef EN_COMBO_NET
        aiot_ais_report_awss_status(AIS_AWSS_STATUS_PROGRESS_REPORT, AIS_AWSS_PROGRESS_CONNECT_AP_SUCCESS);
#endif
    } else if (event->code == CODE_WIFI_ON_DISCONNECT) {
        unsigned long reason_code = event->value;
        printf("CODE_WIFI_ON_DISCONNECT, reason_code = %d\r\n", reason_code);
#ifdef EN_COMBO_NET
        combo_set_ap_state(COMBO_AP_DISCONNECTED);
        aiot_ais_report_awss_status(AIS_AWSS_STATUS_PROGRESS_REPORT, AIS_AWSS_PROGRESS_CONNECT_AP_FAILED | reason_code);
#endif
    } else if (event->code == CODE_WIFI_ON_CONNECT_FAILED) {
        app_err("CODE_WIFI_ON_CONNECT_FAILED");
#ifdef EN_COMBO_NET
        aiot_ais_report_awss_status(AIS_AWSS_STATUS_PROGRESS_REPORT, AIS_AWSS_PROGRESS_CONNECT_AP_FAILED);
#endif
    } else if (event->code == CODE_WIFI_ON_GOT_IP) {
        app_info("CODE_WIFI_ON_GOT_IP");
#ifdef EN_COMBO_NET
        combo_set_ap_state(COMBO_AP_CONNECTED);
        aiot_ais_report_awss_status(AIS_AWSS_STATUS_PROGRESS_REPORT, AIS_AWSS_PROGRESS_GET_IP_SUCCESS);
#endif
    }

    if (event->code != CODE_WIFI_ON_GOT_IP) {
        return;
    }

    netmgr_ap_config_t config;
    memset(&config, 0, sizeof(netmgr_ap_config_t));
    netmgr_get_ap_config(&config);
    app_info("wifi_service_event config.ssid %s", config.ssid);
    if (strcmp(config.ssid, "adha") == 0 || strcmp(config.ssid, "aha") == 0) {
        // clear_wifi_ssid();
        return;
    }
    set_net_state(GOT_AP_SSID);
#ifdef EN_COMBO_NET
    combo_ap_conn_notify();
#endif

    if (!linkkit_started) {
#ifdef CONFIG_PRINT_HEAP
        print_heap();
#endif

        aos_task_new("linkkit", (void (*)(void *))linkkit_main, NULL, 1024 * 6);

        linkkit_started = 1;
    }
}

static void cloud_service_event(input_event_t *event, void *priv_data)
{
    if (event->type != EV_YUNIO) {
        return;
    }

    app_info("cloud_service_event %d", event->code);

    if (event->code == CODE_YUNIO_ON_CONNECTED) {
        app_info("user sub and pub here");
        return;
    }

    if (event->code == CODE_YUNIO_ON_DISCONNECTED) {
    }
}

/*
 * Note:
 * the linkkit_event_monitor must not block and should run to complete fast
 * if user wants to do complex operation with much time,
 * user should post one task to do this, not implement complex operation in
 * linkkit_event_monitor
 */

static void linkkit_event_monitor(int event)
{
    switch (event) {
        case IOTX_AWSS_START: // AWSS start without enbale, just supports device discover
            // operate led to indicate user
            app_info("IOTX_AWSS_START");
            break;
        case IOTX_AWSS_ENABLE: // AWSS enable, AWSS doesn't parse awss packet until AWSS is enabled.
            app_info("IOTX_AWSS_ENABLE");
            // operate led to indicate user
            break;
        case IOTX_AWSS_LOCK_CHAN: // AWSS lock channel(Got AWSS sync packet)
            app_info("IOTX_AWSS_LOCK_CHAN");
            // operate led to indicate user
            break;
        case IOTX_AWSS_PASSWD_ERR: // AWSS decrypt passwd error
            app_info("IOTX_AWSS_PASSWD_ERR");
            // operate led to indicate user
            break;
        case IOTX_AWSS_GOT_SSID_PASSWD:
            app_info("IOTX_AWSS_GOT_SSID_PASSWD");
            // operate led to indicate user
            set_net_state(GOT_AP_SSID);
            break;
        case IOTX_AWSS_CONNECT_ADHA: // AWSS try to connnect adha (device
            // discover, router solution)
            app_info("IOTX_AWSS_CONNECT_ADHA");
            // operate led to indicate user
            break;
        case IOTX_AWSS_CONNECT_ADHA_FAIL: // AWSS fails to connect adha
            app_info("IOTX_AWSS_CONNECT_ADHA_FAIL");
            // operate led to indicate user
            break;
        case IOTX_AWSS_CONNECT_AHA: // AWSS try to connect aha (AP solution)
            app_info("IOTX_AWSS_CONNECT_AHA");
            // operate led to indicate user
            break;
        case IOTX_AWSS_CONNECT_AHA_FAIL: // AWSS fails to connect aha
            app_info("IOTX_AWSS_CONNECT_AHA_FAIL");
            // operate led to indicate user
            break;
        case IOTX_AWSS_SETUP_NOTIFY: // AWSS sends out device setup information
            // (AP and router solution)
            app_info("IOTX_AWSS_SETUP_NOTIFY");
            // operate led to indicate user
            break;
        case IOTX_AWSS_CONNECT_ROUTER: // AWSS try to connect destination router
            app_info("IOTX_AWSS_CONNECT_ROUTER");
            // operate led to indicate user
            break;
        case IOTX_AWSS_CONNECT_ROUTER_FAIL: // AWSS fails to connect destination
            // router.
            app_info("IOTX_AWSS_CONNECT_ROUTER_FAIL");
            set_net_state(CONNECT_AP_FAILED);
            // operate led to indicate user
            break;
        case IOTX_AWSS_GOT_IP: // AWSS connects destination successfully and got
            // ip address
            app_info("IOTX_AWSS_GOT_IP");
            // operate led to indicate user
            break;
        case IOTX_AWSS_SUC_NOTIFY: // AWSS sends out success notify (AWSS
            // sucess)
            app_info("IOTX_AWSS_SUC_NOTIFY");
            // operate led to indicate user
            break;
        case IOTX_AWSS_BIND_NOTIFY: // AWSS sends out bind notify information to
            // support bind between user and device
            app_info("IOTX_AWSS_BIND_NOTIFY");
            // operate led to indicate user
            user_example_ctx_t *user_example_ctx = user_example_get_ctx();
            user_example_ctx->bind_notified = 1;
            break;
        case IOTX_AWSS_ENABLE_TIMEOUT: // AWSS enable timeout
            // user needs to enable awss again to support get ssid & passwd of router
            app_info("IOTX_AWSS_ENALBE_TIMEOUT");
            // operate led to indicate user
            break;
        case IOTX_CONN_CLOUD: // Device try to connect cloud
            app_info("IOTX_CONN_CLOUD");
            // operate led to indicate user
            break;
        case IOTX_CONN_CLOUD_FAIL: // Device fails to connect cloud, refer to
            // net_sockets.h for error code
            app_info("IOTX_CONN_CLOUD_FAIL");
#ifdef EN_COMBO_NET
            combo_set_cloud_state(0);
#endif
            set_net_state(CONNECT_CLOUD_FAILED);
            // operate led to indicate user
            break;
        case IOTX_CONN_CLOUD_SUC: // Device connects cloud successfully
            app_info("IOTX_CONN_CLOUD_SUC");
#ifdef EN_COMBO_NET
            combo_set_cloud_state(1);
#endif
            set_net_state(CONNECT_CLOUD_SUCCESS);
            // operate led to indicate user
            break;
        case IOTX_RESET: // Linkkit reset success (just got reset response from
            // cloud without any other operation)
            app_info("IOTX_RESET");
            break;
        case IOTX_CONN_REPORT_TOKEN_SUC:
#ifdef EN_COMBO_NET
            combo_token_report_notify();
#endif
            app_info("---- report token success ----");
            break;
        default:
            break;
    }
}

#ifdef AWSS_BATCH_DEVAP_ENABLE
#define DEV_AP_ZCONFIG_TIMEOUT_MS  120000 // (ms)
extern void awss_set_config_press(uint8_t press);
extern uint8_t awss_get_config_press(void);
extern void zconfig_80211_frame_filter_set(uint8_t filter, uint8_t fix_channel);
void do_awss_dev_ap();

static aos_timer_t dev_ap_zconfig_timeout_timer;
static uint8_t g_dev_ap_zconfig_timer = 0; // this timer create once and can restart
static uint8_t g_dev_ap_zconfig_run = 0;

static void timer_func_devap_zconfig_timeout(void *arg1, void *arg2)
{
    app_info("%s run\n", __func__);

    if (awss_get_config_press()) {
        // still in zero wifi provision stage, should stop and switch to dev ap
        do_awss_dev_ap();
    } else {
        // zero wifi provision finished
    }

    awss_set_config_press(0);
    zconfig_80211_frame_filter_set(0xFF, 0xFF);
    g_dev_ap_zconfig_run = 0;
    aos_timer_stop(&dev_ap_zconfig_timeout_timer);
}

static void awss_dev_ap_switch_to_zeroconfig(void *p)
{
    app_info("%s run\n", __func__);
    // Stop dev ap wifi provision
    awss_dev_ap_stop();
    // Start and enable zero wifi provision
    iotx_event_regist_cb(linkkit_event_monitor);
    awss_set_config_press(1);

    // Start timer to count duration time of zero provision timeout
    if (!g_dev_ap_zconfig_timer) {
        aos_timer_new(&dev_ap_zconfig_timeout_timer, timer_func_devap_zconfig_timeout, NULL, DEV_AP_ZCONFIG_TIMEOUT_MS, 0);
        g_dev_ap_zconfig_timer = 1;
    }
    aos_timer_start(&dev_ap_zconfig_timeout_timer);

    // This will hold thread, when awss is going
    netmgr_start(true);

    app_info("%s exit\n", __func__);
    aos_task_exit(0);
}

int awss_dev_ap_modeswitch_cb(uint8_t awss_new_mode, uint8_t new_mode_timeout, uint8_t fix_channel)
{
    if ((awss_new_mode == 0) && !g_dev_ap_zconfig_run) {
        g_dev_ap_zconfig_run = 1;
        // Only receive zero provision packets
        zconfig_80211_frame_filter_set(0x00, fix_channel);
        app_info("switch to awssmode %d, mode_timeout %d, chan %d\n", 0x00, new_mode_timeout, fix_channel);
        // switch to zero config
        aos_task_new("devap_to_zeroconfig", awss_dev_ap_switch_to_zeroconfig, NULL, 2048);
    }
}
#endif

static void awss_close_dev_ap(void *p)
{
#ifdef AWSS_SUPPORT_DEV_AP
    awss_dev_ap_stop();
#endif
    awss_dev_ap_started = 0;
    aos_task_exit(0);
}

void awss_open_dev_ap(void *p)
{
    iotx_event_regist_cb(linkkit_event_monitor);
    /*if (netmgr_start(false) != 0) */{
        awss_dev_ap_started = 1;
        //aos_msleep(2000);
#ifdef AWSS_BATCH_DEVAP_ENABLE
        awss_dev_ap_reg_modeswit_cb(awss_dev_ap_modeswitch_cb);
#endif
#ifdef AWSS_SUPPORT_DEV_AP
        awss_dev_ap_start();
#endif
    }
    aos_task_exit(0);
}

void stop_netmgr(void *p)
{
    awss_stop();
    aos_task_exit(0);
}

void start_netmgr(void *p)
{
    /* wait for dev_ap mode stop done */
    do {
        aos_msleep(100);
    } while (awss_dev_ap_started);
    iotx_event_regist_cb(linkkit_event_monitor);
    netmgr_start(true);
    aos_task_exit(0);
}

void do_awss_active(void)
{
    app_info("do_awss_active");
#ifdef WIFI_PROVISION_ENABLED
    extern int awss_config_press();
    awss_config_press();
#endif
}

#ifdef EN_COMBO_NET

void combo_open(void)
{
    combo_net_init();
}

void ble_awss_open(void *p)
{
    iotx_event_regist_cb(linkkit_event_monitor);
    combo_set_awss_state(1);
    aos_task_exit(0);
}

static void ble_awss_close(void *p)
{
    combo_set_awss_state(0);
    aos_task_exit(0);
}

void do_ble_awss()
{
    aos_task_new("ble_awss_open", ble_awss_open, NULL, 2048);
}
#endif

void do_awss_dev_ap()
{
    // Enter dev_ap awss mode
    aos_task_new("netmgr_stop", stop_netmgr, NULL, 4096);
    aos_task_new("dap_open", awss_open_dev_ap, NULL, 4096);
}

void do_awss()
{
    // Enter smart_config awss mode
    aos_task_new("dap_close", awss_close_dev_ap, NULL, 4096);
    aos_task_new("netmgr_start", start_netmgr, NULL, 5120);
}

void linkkit_reset(void *p)
{
    aos_msleep(2000);
#ifdef AOS_TIMER_SERVICE
    timer_service_clear();
#endif
#ifdef AIOT_DEVICE_TIMER_ENABLE
    aiot_device_timer_clear();
#endif
    aos_kv_del(KV_KEY_SWITCH_STATE);
    iotx_sdk_reset_local();
    netmgr_clear_ap_config();
#ifdef EN_COMBO_NET
    breeze_clear_bind_info();
#endif
    HAL_Reboot();
    aos_task_exit(0);
}

extern int iotx_sdk_reset(iotx_vendor_dev_reset_type_t *reset_type);
iotx_vendor_dev_reset_type_t reset_type = IOTX_VENDOR_DEV_RESET_TYPE_UNBIND_ONLY;
void do_awss_reset(void)
{
#ifdef WIFI_PROVISION_ENABLED
    aos_task_new("reset", (void (*)(void *))iotx_sdk_reset, &reset_type, 6144);  // stack taken by iTLS is more than taken by TLS.
#endif
    aos_task_new_ext(&task_linkkit_reset, "reset task", linkkit_reset, NULL, 1024, 0);
}

void reboot_device(void *p)
{
    aos_msleep(500);
    HAL_Reboot();
    aos_task_exit(0);
}

void do_awss_reboot(void)
{
    int ret;
    unsigned char awss_flag = 1;
    int len = sizeof(awss_flag);

    ret = aos_kv_set("awss_flag", &awss_flag, len, 1);
    if (ret != 0)
        app_info("KV Setting failed");

    aos_task_new_ext(&task_reboot_device, "reboot task", reboot_device, NULL, 1024, 0);
}

void linkkit_key_process(input_event_t *eventinfo, void *priv_data)
{
    if (eventinfo->type != EV_KEY) {
        return;
    }
    app_info("awss config press %d\n", eventinfo->value);

    if (eventinfo->code == CODE_BOOT) {
        if (eventinfo->value == VALUE_KEY_CLICK) {
            do_awss_active();
        } else if (eventinfo->value == VALUE_KEY_LTCLICK) {
            do_awss_reset();
        }
    }
}

#ifdef MANUFACT_AP_FIND_ENABLE
void manufact_ap_find_process(int result)
{
    // Informed manufact ap found or not.
    // If manufact ap found, lower layer will auto connect the manufact ap
    // IF manufact ap not found, lower layer will enter normal awss state
    if (result == 0) {
        app_info("%s ap found.\n", __func__);
    } else {
        app_info("%s ap not found.\n", __func__);
    }
}
#endif

#ifdef CONFIG_AOS_CLI
static void handle_reset_cmd(char *pwbuf, int blen, int argc, char **argv)
{
    aos_schedule_call((aos_call_t)do_awss_reset, NULL);
}

static void handle_active_cmd(char *pwbuf, int blen, int argc, char **argv)
{
    aos_schedule_call((aos_call_t)do_awss_active, NULL);
}

static void handle_dev_ap_cmd(char *pwbuf, int blen, int argc, char **argv)
{
    aos_schedule_call((aos_call_t)do_awss_dev_ap, NULL);
}

#ifdef EN_COMBO_NET
static void handle_ble_awss_cmd(char *pwbuf, int blen, int argc, char **argv)
{
    aos_schedule_call((aos_call_t)do_ble_awss, NULL);
}
#endif

static void handle_linkkey_cmd(char *pwbuf, int blen, int argc, char **argv)
{
    if (argc == 1) {
        int len = 0;
        char product_key[PRODUCT_KEY_LEN + 1] = { 0 };
        char product_secret[PRODUCT_SECRET_LEN + 1] = { 0 };
        char device_name[DEVICE_NAME_LEN + 1] = { 0 };
        char device_secret[DEVICE_SECRET_LEN + 1] = { 0 };
        char pidStr[11] = { 0 };

        len = PRODUCT_KEY_LEN + 1;
        aos_kv_get("linkkit_product_key", product_key, &len);

        len = PRODUCT_SECRET_LEN + 1;
        aos_kv_get("linkkit_product_secret", product_secret, &len);

        len = DEVICE_NAME_LEN + 1;
        aos_kv_get("linkkit_device_name", device_name, &len);

        len = DEVICE_SECRET_LEN + 1;
        aos_kv_get("linkkit_device_secret", device_secret, &len);

        aos_cli_printf("Product Key=%s.\r\n", product_key);
        aos_cli_printf("Device Name=%s.\r\n", device_name);
        aos_cli_printf("Device Secret=%s.\r\n", device_secret);
        aos_cli_printf("Product Secret=%s.\r\n", product_secret);
        len = sizeof(pidStr);
        if (aos_kv_get("linkkit_product_id", pidStr, &len) == 0) {
            aos_cli_printf("Product Id=%d.\r\n", atoi(pidStr));
        }
    } else if (argc == 5 || argc == 6) {
        aos_kv_set("linkkit_product_key", argv[1], strlen(argv[1]) + 1, 1);
        aos_kv_set("linkkit_device_name", argv[2], strlen(argv[2]) + 1, 1);
        aos_kv_set("linkkit_device_secret", argv[3], strlen(argv[3]) + 1, 1);
        aos_kv_set("linkkit_product_secret", argv[4], strlen(argv[4]) + 1, 1);
        if (argc == 6)
            aos_kv_set("linkkit_product_id", argv[5], strlen(argv[5]) + 1, 1);
        aos_cli_printf("Done");
    } else {
        aos_cli_printf("Error: %d\r\n", __LINE__);
        return;
    }
}

static void handle_awss_cmd(char *pwbuf, int blen, int argc, char **argv)
{
    aos_schedule_call((aos_call_t)do_awss, NULL);
}



static struct cli_command resetcmd = {
    .name = "reset",
    .help = "factory reset",
    .function = handle_reset_cmd
};

static struct cli_command awss_enable_cmd = {
    .name = "active_awss",
    .help = "active_awss [start]",
    .function = handle_active_cmd
};

static struct cli_command awss_dev_ap_cmd = {
    .name = "dev_ap",
    .help = "awss_dev_ap [start]",
    .function = handle_dev_ap_cmd
};

static struct cli_command awss_cmd = {
    .name = "awss",
    .help = "awss [start]",
    .function = handle_awss_cmd
};

#ifdef EN_COMBO_NET
static struct cli_command awss_ble_cmd = {
    .name = "ble_awss",
    .help = "ble_awss [start]",
    .function = handle_ble_awss_cmd
};

static void handle_ble_status_report_cmd(char *pwbuf, int blen, int argc, char **argv) {

    if (argc == 3) {
        aiot_ais_report_awss_status(atoi(argv[1]), atoi(argv[2]));
    }
}

static struct cli_command ble_status_report_cmd = {
    .name = "ble_report",
    .help = "ble_report [status] [code]",
    .function = handle_ble_status_report_cmd
};
#endif


#ifdef zzc_kv_list

//#include "aiot_al_kv.h"
//#include "aiot_kv_api.h"
#include <hal/soc/flash.h>
#include <ctype.h>

#define KV_TOTAL_SIZE (8192)

/* The physical parition for key-value store */
#ifndef CONFIG_AOS_KV_PTN
#define KV_PTN HAL_PARTITION_PARAMETER_2
#else
#define KV_PTN CONFIG_AOS_KV_PTN
#endif

/* Defination of block information */
#define BLK_BITS 12                          /* The number of bits in block size */
#define BLK_SIZE (1 << BLK_BITS)             /* Block size, current is 4k bytes */
#define BLK_NUMS (KV_TOTAL_SIZE >> BLK_BITS) /* The number of blocks, must be bigger than KV_GC_RESERVED */
#define BLK_OFF_MASK ~(BLK_SIZE - 1)         /* The mask of block offset in key-value store */
#define BLK_STATE_USED 0xCC                  /* Block state: USED --> block is inused and without dirty data */
#define BLK_STATE_CLEAN 0xEE                 /* Block state: CLEAN --> block is clean, ready for used */
#define BLK_STATE_DIRTY 0x44                 /* Block state: DIRTY --> block is inused and with dirty data */
#define BLK_HEADER_SIZE 4                    /* The block header size 4bytes */

#define INVALID_BLK_STATE(state)     \
    (((state) != BLK_STATE_USED) &&  \
     ((state) != BLK_STATE_CLEAN) && \
     ((state) != BLK_STATE_DIRTY))

/* Defination of key-value item information */
#define ITEM_HEADER_SIZE 8     /* The key-value item header size 8bytes */
#define ITEM_STATE_NORMAL 0xEE /* Key-value item state: NORMAL --> the key-value item is valid */
#define ITEM_STATE_DELETE 0    /* Key-value item state: DELETE --> the key-value item is deleted */
#define ITEM_MAX_KEY_LEN 128   /* The max key length for key-value item */

#ifdef AIOT_DEVICE_TIMER_ENABLE
#define ITEM_MAX_VAL_LEN 1536 /* The max value length for key-value item */
#else
#define ITEM_MAX_VAL_LEN 1024 /* The max value length for key-value item */
#endif

#define ITEM_MAX_LEN (ITEM_HEADER_SIZE + ITEM_MAX_KEY_LEN + ITEM_MAX_VAL_LEN)

/* Defination of key-value store information */
#define KV_STATE_OFF 1                      /* The offset of block/item state in header structure */
#define KV_ALIGN_MASK ~(sizeof(void *) - 1) /* The mask of key-value store alignment */
#define KV_GC_RESERVED 1                    /* The reserved block for garbage collection */
#define KV_GC_STACK_SIZE 1024

#define KV_SELF_REMOVE 0
#define KV_ORIG_REMOVE 1

#define KV_MODULE_NAME "kv"

#define KV_VERSION "1.0"
#define KV_VERSION_MAX_LEN (8)
#define KV_VERSION_KEY "kv_verion"

#define KV_GC_TASK_NAME "kv_gc"
#define KV_GC_TASK_STACK_SIZE 4096

    typedef enum _kv_get_type_e
    {
        KV_GET_TYPE_STRING = 1,
        KV_GET_TYPE_BINARY,
        KV_GET_TYPE_INT,
        KV_GET_TYPE_FLOAT,
        KV_GET_TYPE_MAX
    } kv_get_type_e;

    /* Key-value function return code description */
    typedef enum
    {
        RES_OK = 0, /* Successed */
        RES_INIT_FAILED = -1,
        RES_CONT = -EAGAIN,           /* Loop continued */
        RES_NO_SPACE = -ENOSPC,       /* The space is out of range */
        RES_INVALID_PARAM = -EINVAL,  /* The parameter is invalid */
        RES_MALLOC_FAILED = -ENOMEM,  /* Error related to malloc */
        RES_ITEM_NOT_FOUND = -ENOENT, /* Could not find the key-value item */
        RES_FLASH_READ_ERR = -EIO,    /* The flash read operation failed */
        RES_FLASH_WRITE_ERR = -EIO,   /* The flash write operation failed */
        RES_FLASH_EARSE_ERR = -EIO    /* The flash earse operation failed */
    } result_e;

    typedef struct
    {
        char *p;
        int ret;
        uint16_t len;
    } kv_storeage_t;

    /* Flash block header description */
    typedef struct _block_header_t
    {
        uint8_t magic; /* The magic number of block */
        uint8_t state; /* The state of the block */
        uint8_t reserved[2];
    } __attribute__((packed)) block_hdr_t;

    /* Key-value item header description */
    typedef struct _item_header_t
    {
        uint8_t magic;       /* The magic number of key-value item */
        uint8_t state;       /* The state of key-value item */
        uint8_t crc;         /* The crc-8 value of key-value item */
        uint8_t key_len;     /* The length of the key */
        uint16_t val_len;    /* The length of the value */
        uint16_t origin_off; /* The origin key-value item offset, it will be used when updating */
    } __attribute__((packed)) item_hdr_t;

    /* Key-value item description */
    typedef struct _kv_item_t
    {
        item_hdr_t hdr; /* The header of the key-value item, detail see the item_hdr_t structure */
        char *store;    /* The store buffer for key-value */
        uint16_t len;   /* The length of the buffer */
        uint16_t pos;   /* The store position of the key-value item */
    } kv_item_t;

    /* Block information structure for management */
    typedef struct _block_info_t
    {
        uint16_t space; /* Free space in current block */
        uint8_t state;  /* The state of current block */
    } block_info_t;

    typedef struct _kv_handle_t
    {
        uint8_t kv_initialize;  /* The flag to indicate the key-value store is initialized */
        uint8_t gc_triggered;   /* The flag to indicate garbage collection is triggered */
        uint8_t clean_blk_nums; /* The number of block which state is clean */
        uint16_t write_pos;     /* Current write position for key-value item */
        void *kv_mutex;
        block_info_t block_info[BLK_NUMS]; /* The array to record block management information */
#ifndef BUILD_AOS
        int fd;
#ifdef COMPATIBLE_LK_KV
        uint8_t is_lk_kv;
#endif
#endif
    } kv_handle_t;

    static kv_handle_t g_kv_mgr;
    void *gc_thread;

    static const uint8_t BLK_MAGIC_NUM = 'K';  /* The block header magic number */
    static const uint8_t ITEM_MAGIC_NUM = 'I'; /* The key-value item header magic number */

    static void kv_al_free(void *ptr)
    {
    #ifdef BUILD_AOS
        aos_free(ptr);
    #else
        HAL_Free(ptr);
    #endif
    }

    static void *kv_al_malloc(uint32_t size)
    {
    #ifdef BUILD_AOS
        return aos_malloc(size);
    #else
        return HAL_Malloc(size);
    #endif
    }

//    void *aiot_al_mutex_init(void)
//    {
//    #ifdef BUILD_AOS
//        aos_mutex_t mutex;
//        if (0 != aos_mutex_new(&mutex))
//        {
//            return NULL;
//        }
//
//        return mutex.hdl;
//    #else
//        return HAL_MutexCreate();
//    #endif
//    }
//
//    void aiot_al_mutex_lock(void *mutex)
//    {
//    #ifdef BUILD_AOS
//        aos_mutex_lock((aos_mutex_t *)&mutex, AOS_WAIT_FOREVER);
//    #else
//        HAL_MutexLock(mutex);
//    #endif
//    }
//
//    void aiot_al_mutex_unlock(void *mutex)
//    {
//    #ifdef BUILD_AOS
//        aos_mutex_unlock((aos_mutex_t *)&mutex);
//    #else
//        HAL_MutexUnlock(mutex);
//    #endif
//    }
//
//    void aiot_al_mutex_deinit(void **mutex)
//    {
//    #ifdef BUILD_AOS
//        if (NULL != mutex)
//        {
//            aos_mutex_free(*(aos_mutex_t **)mutex);
//            *mutex = NULL;
//        }
//    #else
//        HAL_MutexDestroy(mutex);
//    #endif
//    }

    /* CRC-8: the poly is 0x31 (x^8 + x^5 + x^4 + 1) */
    static uint8_t utils_crc8(uint8_t *buf, uint16_t length)
    {
        uint8_t crc = 0x00;
        uint8_t i;

        while (length--)
        {
            crc ^= *buf++;
            for (i = 8; i > 0; i--)
            {
                if (crc & 0x80)
                {
                    crc = (crc << 1) ^ 0x31;
                }
                else
                {
                    crc <<= 1;
                }
            }
        }

        return crc;
    }

    static int raw_read(uint32_t offset, void *buf, size_t nbytes)
    {
    #if defined(BUILD_AOS)
        return hal_flash_read((hal_partition_t)KV_PTN, (uint32_t *)&offset, buf, nbytes);
    #else
        return aiot_al_kv_flash_read(&offset, buf, nbytes);
    #endif
    }

    static int raw_write(uint32_t offset, const void *buf, size_t nbytes)
    {
    #if defined(BUILD_AOS)
        return hal_flash_write((hal_partition_t)KV_PTN, (uint32_t *)&offset, buf, nbytes);
    #else
        return aiot_al_kv_flash_write(&offset, buf, nbytes);
    #endif
    }

    static int raw_erase(uint32_t offset, uint32_t size)
    {
    #if defined(BUILD_AOS)
        return hal_flash_erase((hal_partition_t)KV_PTN, offset, size);
    #else
        return aiot_al_kv_flash_erase(offset, size);
    #endif
    }
    static void kv_item_free(kv_item_t *item)
    {
        if (item)
        {
            if (item->store)
            {
                kv_al_free(item->store);
            }
            kv_al_free(item);
        }
    }

    static int kv_state_set(uint16_t pos, uint8_t state)
    {
        return raw_write(pos + KV_STATE_OFF, &state, 1);
    }


static int _is_printable_string(const char *string, unsigned int len)
{
    while (len)
    {
        if (!isprint(string[len - 1]))
        {
            return 0;
        }
        len--;
    }
    return 1;
}

static int __item_print_cb(kv_item_t *item, const char *key)
{
    int index = 0;
    char *p_key = NULL;
    char *p_val = NULL;
    p_key = (char *)kv_al_malloc(item->hdr.key_len + 1);
    if (!p_key)
    {
        return RES_MALLOC_FAILED;
    }
    memset(p_key, 0, item->hdr.key_len + 1);
    raw_read(item->pos + ITEM_HEADER_SIZE, p_key, item->hdr.key_len);

    p_val = (char *)kv_al_malloc(item->hdr.val_len + 1);
    if (!p_val)
    {
        kv_al_free(p_key);
        return RES_MALLOC_FAILED;
    }
    memset(p_val, 0, item->hdr.val_len + 1);
    raw_read(item->pos + ITEM_HEADER_SIZE + item->hdr.key_len, p_val, item->hdr.val_len);

    if (_is_printable_string(p_val, item->hdr.val_len))
    {
        aos_cli_printf("\r\nkey:%s vaule[%d]:%s\r\n", p_key, item->hdr.val_len, p_val);
    }
    else
    {
        aos_cli_printf("\r\nkey:%s binary value[%d] is\r\n", p_key, item->hdr.val_len);

        for (index = 0; index < item->hdr.val_len; index++)
        {
            aos_cli_printf("0x%02x ", (unsigned char)p_val[index]);
            if ((index + 1) % 16 == 0)
            {
                aos_cli_printf("\r\n");
            }
        }
    }

    kv_al_free(p_key);
    kv_al_free(p_val);

    return RES_CONT;
}

typedef int (*item_func)(kv_item_t *item, const char *key);

static kv_item_t *kv_item_traverse(item_func func, uint8_t blk_index, const char *key)
{
    kv_item_t *item;
    item_hdr_t *hdr;
    uint16_t pos = (blk_index << BLK_BITS) + BLK_HEADER_SIZE;
    uint16_t end = (blk_index << BLK_BITS) + BLK_SIZE;
    uint16_t len = 0;
    int ret;

    do
    {
        item = (kv_item_t *)kv_al_malloc(sizeof(kv_item_t));
        if (!item)
        {
            return NULL;
        }
        memset(item, 0, sizeof(kv_item_t));
        hdr = &(item->hdr);

        if (raw_read(pos, hdr, ITEM_HEADER_SIZE) != RES_OK)
        {
            kv_item_free(item);
            return NULL;
        }

        if (hdr->magic != ITEM_MAGIC_NUM)
        {
            if ((hdr->magic == 0xFF) && (hdr->state == 0xFF))
            {
                kv_item_free(item);
                break;
            }
            hdr->val_len = 0xFFFF;
        }

        if (hdr->val_len > ITEM_MAX_VAL_LEN || hdr->key_len > ITEM_MAX_KEY_LEN ||
            hdr->val_len == 0 || hdr->key_len == 0)
        {
            pos += ITEM_HEADER_SIZE;
            kv_item_free(item);
            if (g_kv_mgr.block_info[blk_index].state == BLK_STATE_USED)
            {
                kv_state_set((blk_index << BLK_BITS), BLK_STATE_DIRTY);
                g_kv_mgr.block_info[blk_index].state = BLK_STATE_DIRTY;
            }
            continue;
        }

        len = (ITEM_HEADER_SIZE + hdr->key_len + hdr->val_len + ~KV_ALIGN_MASK) & KV_ALIGN_MASK;

        if (hdr->state == ITEM_STATE_NORMAL)
        {
            item->pos = pos;
            item->len = hdr->key_len + hdr->val_len;
            ret = func(item, key);
            if (ret == RES_OK)
            {
                return item;
            }
            else if (ret != RES_CONT)
            {
                kv_item_free(item);
                return NULL;
            }
        }
        else
        {
            if (g_kv_mgr.block_info[blk_index].state == BLK_STATE_USED)
            {
                kv_state_set((blk_index << BLK_BITS), BLK_STATE_DIRTY);
                g_kv_mgr.block_info[blk_index].state = BLK_STATE_DIRTY;
            }
        }

        kv_item_free(item);
        pos += len;
    } while (end > (pos + ITEM_HEADER_SIZE));

    g_kv_mgr.block_info[blk_index].space = (end > pos) ? (end - pos) : ITEM_HEADER_SIZE;

    return NULL;
}

static void handle_list_cmd(char *pwbuf, int blen, int argc, char **argv)
{
	unsigned int i=0;
	for (i = 0; i < BLK_NUMS; i++)
	{
		kv_item_traverse(__item_print_cb, i, NULL);
	}
}

static struct cli_command listcmd = {
    .name = "kvlist",
    .help = "kvlist",
    .function = handle_list_cmd
};


#endif//zzc_kv_list zzc

static struct cli_command linkkeycmd = {
    .name = "linkkey",
    .help = "set/get linkkit keys. linkkey [<Product Key> <Device Name> <Device Secret> <Product Secret>]",
    .function = handle_linkkey_cmd
};

#endif

#ifdef CONFIG_PRINT_HEAP
static void duration_work(void *p)
{
    print_heap();
    aos_post_delayed_action(5000, duration_work, NULL);
}
#endif

#if defined(OTA_ENABLED) && defined(BUILD_AOS)
static int ota_init(void);
static ota_service_t ctx = {0};
static bool ota_service_inited = false;
#ifdef CERTIFICATION_TEST_MODE
void *ct_entry_get_uota_ctx(void)
{
    if (ota_service_inited == true)
    {
        return (void *)&ctx;
    }
    else
    {
        return NULL;
    }
}
#endif
#endif
static int mqtt_connected_event_handler(void)
{
#if defined(OTA_ENABLED) && defined(BUILD_AOS)
    if (ota_service_inited == true) {
        int ret = 0;

        app_info("MQTT reconnected, let's redo OTA upgrade");
        if ((ctx.h_tr) && (ctx.h_tr->upgrade)) {
            app_info("Redoing OTA upgrade");
            ret = ctx.h_tr->upgrade(&ctx);
            if (ret < 0) app_info("Failed to do OTA upgrade");
        }

        return ret;
    }

    app_info("MQTT Construct  OTA start to inform");
#ifdef DEV_OFFLINE_OTA_ENABLE
    ota_service_inform(&ctx);
#else
    ota_init();
#endif

#ifdef OTA_MULTI_MODULE_DEBUG
    extern ota_hal_module_t ota_hal_module1;
    extern ota_hal_module_t ota_hal_module2;
    iotx_ota_module_info_t module;
    char module_name_key[MODULE_NAME_LEN + 1] = {0};
    char module_version_key[MODULE_VERSION_LEN + 1] = {0};
    char module_name_value[MODULE_NAME_LEN + 1] = {0};
    char module_version_value[MODULE_VERSION_LEN + 1] = {0};
    char buffer_len = 0;
    int ret = 0;

    for(int i = 1; i <= 2; i++){
        memset(module_name_key, 0, MODULE_NAME_LEN);
        memset(module_version_key, 0, MODULE_VERSION_LEN);
        memset(module_name_value, 0, MODULE_NAME_LEN);
        memset(module_version_value, 0, MODULE_VERSION_LEN);
        HAL_Snprintf(module_name_key, MODULE_NAME_LEN, "ota_m_name_%d", i);
        HAL_Snprintf(module_version_key, MODULE_VERSION_LEN, "ota_m_version_%d", i);
        HAL_Printf("module_name_key is %s\n",module_name_key);
        HAL_Printf("module_version_key is %s\n",module_version_key);
        buffer_len = MODULE_NAME_LEN;
        ret = HAL_Kv_Get(module_name_key,module_name_value, &buffer_len);
        buffer_len = MODULE_VERSION_LEN;
        ret |= HAL_Kv_Get(module_version_key,module_version_value, &buffer_len);
        memcpy(module.module_name, module_name_value, MODULE_NAME_LEN);
        memcpy(module.module_version, module_version_value, MODULE_VERSION_LEN);
        memcpy(module.product_key, ctx.pk, sizeof(ctx.pk)-1);
        memcpy(module.device_name, ctx.dn, sizeof(ctx.dn)-1);
        if(!ret){
            if(i == 1){
                module.hal = &ota_hal_module1;
            }else{
                module.hal = &ota_hal_module2;
            }
            ota_service_set_module_info(&ctx, &module);
        }
        HAL_Printf("module_name_value is %s\n",module_name_value);
        HAL_Printf("module_version_value is %s\n",module_version_value);
    }

#endif
    ota_service_inited = true;
#endif
    return 0;
}

static int ota_init(void)
{
#if defined(OTA_ENABLED) && defined(BUILD_AOS)
    char product_key[PRODUCT_KEY_LEN + 1] = {0};
    char device_name[DEVICE_NAME_LEN + 1] = {0};
    char device_secret[DEVICE_SECRET_LEN + 1] = {0};
    HAL_GetProductKey(product_key);
    HAL_GetDeviceName(device_name);
    HAL_GetDeviceSecret(device_secret);
    memset(&ctx, 0, sizeof(ota_service_t));
    strncpy(ctx.pk, product_key, sizeof(ctx.pk)-1);
    strncpy(ctx.dn, device_name, sizeof(ctx.dn)-1);
    strncpy(ctx.ds, device_secret, sizeof(ctx.ds)-1);
    ctx.trans_protcol = 0;
    ctx.dl_protcol = 3;
    ota_service_init(&ctx);
#endif
    return 0;
}

static void show_firmware_version(void)
{
    printf("--------Firmware info--------\r\n");
    app_crit("app: %s,  board: %s", APP_NAME, PLATFORM);
    app_crit("Host: %s", COMPILE_HOST);
    app_crit("Branch: %s", GIT_BRANCH);
    app_crit("Hash: %s", GIT_HASH);
    app_crit("Date: %s %s", __DATE__, __TIME__);
    app_crit("Kernel: %s", aos_get_kernel_version());
    app_crit("LinkKit: %s", LINKKIT_VERSION);
    app_crit("APP: %s", aos_get_app_version());
    app_crit("Region env: %s", REGION_ENV_STRING);
}

#define zzc
#ifdef zzc
#include "hal/soc/uart.h"
//uart_dev_t   uart_app;
//void lj_usart_init(void)
//{
//
//	    uint8_t pins[] = {4, 3}; // {tx_pin, rx_pin}
//
//	    uart_app.port = 1;
//	    uart_app.config.baud_rate = 9600;
//	    uart_app.priv = pins;
//	    hal_uart_init(&uart_app);
//
//	    unsigned char bb[]={0x01,0x02};
//	    hal_uart_send(&uart_app,bb,2,0);
//}

#ifdef _zijian2_
char zj2[2]={0};
int zj2_len=0;
#endif

#ifdef _power_safe_
char pw[2]={0};
int pw_len=0;
#endif

char quite_restart_buf[2]={0};
int quite_restart_buf_len=2;

extern void lj_key_detect_event_task(void *arg);

extern void lj_load_zhufan_total_time(void);

#endif//zzc

int application_start(int argc, char **argv)
{
#if (defined (TG7100CEVB))
    //media_to_kv();
#endif

#ifdef CONFIG_PRINT_HEAP
    print_heap();
    aos_post_delayed_action(5000, duration_work, NULL);
#endif

#ifdef CSP_LINUXHOST
    signal(SIGPIPE, SIG_IGN);
#endif

#ifdef WITH_SAL
    sal_init();
#endif

#ifdef MDAL_MAL_ICA_TEST
    HAL_MDAL_MAL_Init();
#endif

#ifdef DEFAULT_LOG_LEVEL_DEBUG
    IOT_SetLogLevel(IOT_LOG_DEBUG);
#else
    IOT_SetLogLevel(IOT_LOG_INFO);
#endif

    show_firmware_version();
    /* Must call set_device_meta_info before netmgr_init */
    set_device_meta_info();

#ifdef zzc

#ifdef _sai_

lj_timer_init();
#ifdef _sai_test_
lj_usart_init();


#endif //_sai_test_
#else //sai
  lj_usart_init();
#endif//sai


#ifdef _chaoren_jhq_
char chaoren_pk[PRODUCT_KEY_LEN + 1]={0};
int c_len=PRODUCT_KEY_LEN+1;
    if(mkv_item_get("linkkit_product_key", chaoren_pk, &c_len)==0)
    {
    	if(memcmp(chaoren_pk,"a1fItqK6e5O",strlen(chaoren_pk))==0)
    	{
    		chaoren_kind=0;
    		app_info("match chaoren_kind=%d",chaoren_kind);
    	}
    	else if(memcmp(chaoren_pk,"a1E7cOzGqG8",strlen(chaoren_pk))==0)
    	{
    		chaoren_kind=1;
    		app_info("match chaoren_kind=%d",chaoren_kind);
    	}
    	else if(memcmp(chaoren_pk,"a1TtR6SjdYs",strlen(chaoren_pk))==0)
		{
			chaoren_kind=2;
			app_info("match chaoren_kind=%d",chaoren_kind);
		}
    }
    else
    {
    	chaoren_kind=0;
    	app_info("not match chaoren_kind=%d",chaoren_kind);
    }
    if(chaoren_kind==0)
    {
    	DEC_KIND=0xe8;
    	MCU_VER=0;
    }
    else if(chaoren_kind==1)
    {
    	DEC_KIND=0xe1;
		MCU_VER=0;
    }
    else if(chaoren_kind==2)
	{
		DEC_KIND=0xe2;
		MCU_VER=0;
	}
    app_info("DEC_KIND=%02x,MCU_VER=%02x",DEC_KIND,MCU_VER);
#endif

#ifdef _i2c_
    lj_i2c_init();
#endif


    demo_pkt_st_init();


#ifdef _zijian2_
    zj2_len=2;
	// aos_kv_get("power_down",&pw,&pw_len);
    app_info("GET is =%d,zj2[0]=%02x",aos_kv_get("zijian2",&zj2,&zj2_len),zj2[0]);
	if(zj2[0]=='2')//杈撳叆鏄瓧绗︿覆锛岃緭鍑烘槸鏁板瓧锛�
	{
		aos_kv_del("zijian2");
		app_info("GET IN TO zijian2");
		do_zijain();
#ifndef zijian_reset
#else
		goto exit;
#endif
	}
#endif

#ifdef _use_caipu_
	lj_set_load();//20240227
	lj_load_zhufan_total_time();
#endif //_use_caipu_

#ifdef _power_safe_
    pw_len=2;
    // aos_kv_get("power_down",&pw,&pw_len);
    app_info("GET is =%d,pw[0]=%02x",aos_kv_get("power_down",&pw,&pw_len),pw[0]);
    if(pw[0]=='2')//杈撳叆鏄瓧绗︿覆锛岃緭鍑烘槸鏁板瓧锛�
    {
    	app_info("GET IN TO POWER_DOWN");
        goto exit;
    }
#endif


#endif//zzc



    netmgr_init();
    vendor_product_init();

#ifdef zzc
  netmgr_ap_config_t config;
    memset(&config, 0, sizeof(netmgr_ap_config_t));
    if(netmgr_get_ap_config(&config)==-1)
    {
        LOG("lj_netmgr_get_ap_config.................=-1");
        all_net=0;
#ifdef _sai_
        app_info("read quite_restart is =%d,quite_restart_buf[0]=%02x---------",aos_kv_get("quite_restart",quite_restart_buf ,&quite_restart_buf_len),quite_restart_buf[0]);
//        aos_kv_get("quite_restart",quite_restart_buf ,2,1);
        if(quite_restart_buf[0]=='1')
        {
        	app_info("GET IN TO quite_restart");
        	all_wifi=0;
        	aos_kv_del("quite_restart");
        }
        else
        {
        	all_wifi=3;
        }

#else
        all_wifi=3;
#endif//_sai_
       lj_usend_wifi_net_status(all_wifi,all_net);
    }
#endif


    dev_diagnosis_module_init();
#ifdef DEV_OFFLINE_OTA_ENABLE
    ota_init();
#endif

#ifdef MANUFACT_AP_FIND_ENABLE
    netmgr_manuap_info_set("TEST_AP", "TEST_PASSWORD", manufact_ap_find_process);
#endif

    aos_register_event_filter(EV_KEY, linkkit_key_process, NULL);
    aos_register_event_filter(EV_WIFI, wifi_service_event, NULL);
    aos_register_event_filter(EV_YUNIO, cloud_service_event, NULL);
    IOT_RegisterCallback(ITE_MQTT_CONNECT_SUCC,mqtt_connected_event_handler);

#ifdef CONFIG_AOS_CLI
    aos_cli_register_command(&resetcmd);
    aos_cli_register_command(&awss_enable_cmd);
    aos_cli_register_command(&awss_dev_ap_cmd);
    aos_cli_register_command(&awss_cmd);
#ifdef EN_COMBO_NET
    aos_cli_register_command(&awss_ble_cmd);
    aos_cli_register_command(&ble_status_report_cmd);
#endif
    aos_cli_register_command(&linkkeycmd);
#endif

#ifdef zzc_kv_list
    aos_cli_register_command(&listcmd);
#endif//zzc_kv_list


#ifdef EN_COMBO_NET
    combo_open();
#endif

    init_awss_flag();
#ifdef REPORP_ON_DEMAND
    init_report_service();
#endif
    aos_task_new_ext(&task_key_detect, "detect key pressed", key_detect_event_task, NULL, 1024, AOS_DEFAULT_APP_PRI);

    init_msg_queue();
    aos_task_new_ext(&task_msg_process, "cmd msg process", msg_process_task, NULL, 2048, AOS_DEFAULT_APP_PRI - 1);
#ifdef REPORT_MULTHREAD
    aos_task_new_ext(&task_property_report, "property report", process_property_report_task, NULL, 2048, AOS_DEFAULT_APP_PRI);
#endif
    check_factory_mode();
#ifdef zzc
exit:
#endif//zzc
    aos_loop_run();

    return 0;
}
